<?php

    #-------------------------------------------------------------#
	# This file is part of the Tekuna MVC Microframework for PHP. #
	# (c) 2009 Niels Jäckel <niels.jaeckel@tekuna.org>            #
	# Please visit http://tekuna.org/ for more information.       #
	#                                                             #
	# This software is licensed under the MIT License. Please     #
	# view the LICENSE file for the full text of this license.    #
    #-------------------------------------------------------------#


	// using here requires because of the early load time in the bootstrap process
	require_once TEKUNA_ROOT .'org/tekuna/base/exceptions/TekunaClassLoadException.class.php';
	require_once TEKUNA_ROOT .'org/tekuna/base/exceptions/ClassNotFoundException.class.php';


	/**
	 * Class for loading class files that are located in the
	 * file system tree below the defined base directory. By
	 * convention, the classes are named
	 *
	 *   - CLASSNAME.class.php,
	 *   - CLASSNAME.abstract.php or
	 *   - CLASSNAME.interface.php
	 *
	 * and contain the specified class or interface. The filenames
	 * are case sensitive.
	 */

	class TekunaClassLoader {

		protected
			$sBaseDir = '',
			$arrClasses = array();


		/**
		 * Build the new instance of the class loader. The given base
		 * directory is used for the file search.
		 *
		 * @param string $sBaseDir the base directory
		 */

		public function __construct($sBaseDir) {

			$this -> sBaseDir = $sBaseDir;
			$this -> arrClasses = array_merge(get_declared_classes(), get_declared_interfaces());
		}


		/**
		 * Loads the specified class by converting the given canonical
		 * class name to a file system path. Example:
		 *
		 * org.tekuna.base.TekunaClassLoader -> /org/tekuna/base/TekunaClassLoader.class.php
		 *
		 * If the specified class name (the part after the last point) still
		 * exists in the global loaded classes scope, nothing is done. If
		 * the class load fails, a TekunaClassLoadException is thrown.
		 *
		 * @param string $sFullQualifiedClass the class with path
		 */

		public function loadClass($sFullQualifiedClass) {

			// extract class name
			$sClassName = $sFullQualifiedClass;
			if (FALSE !== $iPosition = strrpos($sFullQualifiedClass, '.')) {

				$sClassName = substr($sFullQualifiedClass, $iPosition + 1);
			}

			// try to load the class if it doesn't still exist
			if (! in_array($sClassName, $this -> arrClasses)) {

				// build base path
				$sClassPath = str_replace('.', DIRECTORY_SEPARATOR, $sFullQualifiedClass);
				$sClassPath = $this -> sBaseDir . $sClassPath;
				$sClassPath = str_replace(DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, $sClassPath);

				// class file options
				$sClassFile1 = $sClassPath .'.class.php';
				$sClassFile2 = $sClassPath .'.abstract.php';
				$sClassFile3 = $sClassPath .'.interface.php';

				// cascaded try to load the class
				if (file_exists($sClassFile1) && is_readable($sClassFile1)) {

					require_once $sClassFile1;
				}
				else if (file_exists($sClassFile2) && is_readable($sClassFile2)) {

						require_once $sClassFile2;
					}
					else if (file_exists($sClassFile3) && is_readable($sClassFile3)) {

							require_once $sClassFile3;
						}
						else {

							throw new TekunaClassLoadException("The class or interface '$sFullQualifiedClass' cloud not be loaded.");
						}

				// append successfully loaded class to loaded classes list
				$this -> arrClasses[] = $sClassName;

				// check if the class now really exists
				if (! class_exists($sClassName) && ! interface_exists($sClassName)) {

					throw new TekunaClassLoadException("The class or interface '$sFullQualifiedClass' cloud not be loaded (but class file successfully included).");
				}
			}
		}
	}

